
;(function(factory) {
	if(typeof define === 'function' && define.amd) {
		define(['jquery'], factory);
	}else {
		factory(jQuery, window);
	}
}(function($, global) {
	'use strict';
	var XFrame_Graph = {}, graph_renderer = null, BaseDrawer = null, RenderLinkedList = null, RenderStructure = null, 
	       findPreZi = null, findPreZiHelper = null, CanvasDrawer = null, VmlDrawer = null, checkStatus = null, 
	       setStatus = null, imageCacher = null, ImageObj, _EF = null, _colorMap = null, _parseColor2RGBA = null, _getMouseSpPosition = null,
           __super = null, __extend = null, __instanceof = null;
	_colorMap = {
	               aliceblue : "#F0F8FF",
                    antiquewhite : "#FAEBD7",
                    aqua : "#00FFFF",
                    aquamarine : "#7FFFD4",
                    azure : "#F0FFFF",
                    beige : "#F5F5DC",
                    bisque : "#FFE4C4",
                    black : "#000000",
                    blanchedalmond : "#FFEBCD",
                    blue : "#0000FF",
                    blueviolet : "#8A2BE2",
                    brown : "#A52A2A",
                    burlywood : "#DEB887",
                    cadetblue : "#5F9EA0",
                    chartreuse : "#7FFF00",
                    chocolate : "#D2691E",
                    coral : "#FF7F50",
                    cornflowerblue : "#6495ED",
                    cornsilk : "#FFF8DC",
                    crimson : "#DC143C",
                    cyan : "#00FFFF",
                    darkblue : "#00008B",
                    darkcyan : "#008B8B",
                    darkgoldenrod : "#B8860B",
                    darkgray : "#A9A9A9",
                    darkgreen : "#006400",
                    darkkhaki : "#BDB76B",
                    darkmagenta : "#8B008B",
                    darkolivegreen : "#556B2F",
                    darkorange : "#FF8C00",
                    darkorchid : "#9932CC",
                    darkred : "#8B0000",
                    darksalmon : "#E9967A",
                    darkseagreen : "#8FBC8B",
                    darkslateblue : "#483D8B",
                    darkslategray : "#2F4F4F",
                    darkturquoise : "#00CED1",
                    darkviolet : "#9400D3",
                    deeppink : "#FF1493",
                    deepskyblue : "#00BFFF",
                    dimgray : "#696969",
                    dodgerblue : "#1E90FF",
                    firebrick : "#B22222",
                    floralwhite : "#FFFAF0",
                    forestgreen : "#228B22",
                    fuchsia : "#FF00FF",
                    gainsboro : "#DCDCDC",
                    ghostwhite : "#F8F8FF",
                    gold : "#FFD700",
                    goldenrod : "#DAA520",
                    gray : "#808080",
                    green : "#008000",
                    greenyellow : "#ADFF2F",
                    honeydew : "#F0FFF0",
                    hotpink : "#FF69B4",
                    indianred : "#CD5C5C",
                    indigo : "#4B0082",
                    ivory : "#FFFFF0",
                    khaki : "#F0E68C",
                    lavender : "#E6E6FA",
                    lavenderblush : "#FFF0F5",
                    lawngreen : "#7CFC00",
                    lemonchiffon : "#FFFACD",
                    lightblue : "#ADD8E6",
                    lightcoral : "#F08080",
                    lightcyan : "#E0FFFF",
                    lightgoldenrodyellow : "#FAFAD2",
                    lightgreen : "#90EE90",
                    lightgrey : "#D3D3D3",
                    lightpink : "#FFB6C1",
                    lightsalmon : "#FFA07A",
                    lightseagreen : "#20B2AA",
                    lightskyblue : "#87CEFA",
                    lightslategray : "#778899",
                    lightsteelblue : "#B0C4DE",
                    lightyellow : "#FFFFE0",
                    lime : "#00FF00",
                    limegreen : "#32CD32",
                    linen : "#FAF0E6",
                    magenta : "#FF00FF",
                    maroon : "#800000",
                    mediumaquamarine : "#66CDAA",
                    mediumblue : "#0000CD",
                    mediumorchid : "#BA55D3",
                    mediumpurple : "#9370DB",
                    mediumseagreen : "#3CB371",
                    mediumslateblue : "#7B68EE",
                    mediumspringgreen : "#00FA9A",
                    mediumturquoise : "#48D1CC",
                    mediumvioletred : "#C71585",
                    midnightblue : "#191970",
                    mintcream : "#F5FFFA",
                    mistyrose : "#FFE4E1",
                    moccasin : "#FFE4B5",
                    navajowhite : "#FFDEAD",
                    navy : "#000080",
                    oldlace : "#FDF5E6",
                    olive : "#808000",
                    olivedrab : "#6B8E23",
                    orange : "#FFA500",
                    orangered : "#FF4500",
                    orchid : "#DA70D6",
                    palegoldenrod : "#EEE8AA",
                    palegreen : "#98FB98",
                    paleturquoise : "#AFEEEE",
                    palevioletred : "#DB7093",
                    papayawhip : "#FFEFD5",
                    peachpuff : "#FFDAB9",
                    peru : "#CD853F",
                    pink : "#FFC0CB",
                    plum : "#DDA0DD",
                    powderblue : "#B0E0E6",
                    purple : "#800080",
                    red : "#FF0000",
                    rosybrown : "#BC8F8F",
                    royalblue : "#4169E1",
                    saddlebrown : "#8B4513",
                    salmon : "#FA8072",
                    sandybrown : "#F4A460",
                    seagreen : "#2E8B57",
                    seashell : "#FFF5EE",
                    sienna : "#A0522D",
                    silver : "#C0C0C0",
                    skyblue : "#87CEEB",
                    slateblue : "#6A5ACD",
                    slategray : "#708090",
                    snow : "#FFFAFA",
                    springgreen : "#00FF7F",
                    steelblue : "#4682B4",
                    tan : "#D2B48C",
                    teal : "#008080",
                    thistle : "#D8BFD8",
                    tomato : "#FF6347",
                    turquoise : "#40E0D0",
                    violet : "#EE82EE",
                    wheat : "#F5DEB3",
                    white : "#FFFFFF",
                    whitesmoke : "#F5F5F5",
                    yellow : "#FFFF00",
                    yellowgreen : "#9ACD32"
               };
	
	_parseColor2RGBA = function(color, alpha){
	    if(typeof color !== 'string') return color;
	    
	    color = _colorMap[color.toLowerCase()] | color;
        var c = /^#([0-9a-fA-F]{3})$|^#([0-9a-fA-F]{6})$/.exec(color), fullColorValue = [], res = "rgba(_R,_G,_B,_A)";
        alpha = alpha || 1;
        if(c){
            if(c[1]){
                res = res.replace("_R", parseInt(c[1].charAt(0) + c[1].charAt(0), 16));
                res = res.replace("_G", parseInt(c[1].charAt(1) + c[1].charAt(1), 16));
                res = res.replace("_B", parseInt(c[1].charAt(2) + c[1].charAt(2), 16));
                res = res.replace("_A", alpha);
            }else{
                res = res.replace("_R", parseInt(c[2].substring(0,2), 16));
                res = res.replace("_G", parseInt(c[2].substring(2,4), 16));
                res = res.replace("_B", parseInt(c[2].substring(4,6), 16));
                res = res.replace("_A", alpha);
            }
            return res;
        }
        return "";
    };

    XFrame_Graph._createClass = function(configureObj, superClass){
        var F = function(){
            var args = arguments;
            $.each(this._fullInits, $.proxy(function(idx, initfn){
                initfn.apply(this, args);
            } ,this));
        };
        var superObj = superClass;
        if($.isFunction(superClass)){
            superObj = superClass.prototype;
        }
        if(superClass){
            F.prototype = $.extend({}, superObj, configureObj);
            F.prototype._superClass = $.isFunction(superClass) ? superClass : superClass.constructor;
        }else{
            F.prototype = configureObj;
        }

        F.prototype._fullInits = F.prototype._superClass ? F.prototype._superClass.prototype._fullInits.slice() : [];
        F.prototype.init && F.prototype._fullInits.push(F.prototype.init);

        F.prototype._myClass = F;
        F.prototype.instanceOf = __instanceof;
        F.prototype.super = __super;
        F.prototype.constructor = F;

        F.extend = __extend;

        return F;
    };


    __super = function(fnName){
        var targetPrototype = null;
        if(targetPrototype = this._myClass.prototype._superClass){
            targetPrototype = targetPrototype.prototype;
        }else{
            targetPrototype = this._myClass.prototype;
        }
        if(targetPrototype[fnName]) {
            return targetPrototype[fnName].apply(this, Array.prototype.slice(arguments, 1));
        }else{
            //TODO 考虑多层级关系
            return undefined;
        }
    };

    __extend = function(subClass){
        return $.isFunction(subClass) ? XFrame_Graph._createClass(subClass.prototype, this) : XFrame_Graph._createClass(subClass, this);
    };

    __instanceof = function(someClass){
        var tmpConstructor = this.constructor;
        if(!someClass){
            return false;
        }
        if(someClass === Object){
            return true;
        }
        if(!$.isFunction(someClass)){
            someClass = someClass.constructor;
        }
        while(tmpConstructor){
            if(tmpConstructor === someClass){
                return true;
            }else{
                tmpConstructor = tmpConstructor.prototype._superClass;
            }
        }
        return false;
    };

	XFrame_Graph._genId = function(){
		return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
			var r = Math.random() * 16|0, v = c === 'x' ? r : (r&0x3|0x8);
			return v.toString(16);
		});
	};

	XFrame_Graph.registShape = function(shape){
		var shapeRs, parentRs;
		if(graph_renderer.allShapes[shape.uuid]){
			return;
		}
		graph_renderer.allShapes[shape.uuid] = shape;
		if(shape.zcontext){
			if(!(parentRs = graph_renderer.allRenderStructure[shape.zcontext])){
				return;
			}
			shape.parentRs = parentRs;
			!parentRs.subList && (parentRs.subList = new RenderLinkedList());
			shapeRs = parentRs.subList.add(shape);

		}else{
			shapeRs = graph_renderer.mainList.add(shape);
			
		}
		graph_renderer.allRenderStructure[shapeRs.id] = shapeRs;
		
		if(shape.showImmediately){
			XFrame_Graph.refresh();
		}
	};
	
	XFrame_Graph.registImage = function(imgurl){
		imageCacher.addImg(imgurl);
	};

	XFrame_Graph.reGetCommands = function(uuid){
		var thisrs ;
		if(thisrs = graph_renderer.allRenderStructure[uuid]){
			thisrs.reGenCommands();
		}
	};
	
	XFrame_Graph.show = XFrame_Graph.refresh = function(){
		
		graph_renderer.render(true);
	};
	

	XFrame_Graph.initCanvas = function(sizzleSel, disEvents){
		var $canvas = $(sizzleSel);
		if($canvas.length){
			graph_renderer.canvasNode = $canvas[0];
			
			var el = document.createElement('canvas'), $tmpnode;
			if(el.getContext && el.getContext('2d')){
				if(graph_renderer.canvasNode.tagName.toLowerCase() !== 'canvas'){
					$tmpnode = $(graph_renderer.canvasNode);
					el.width = $tmpnode.width();
					el.height = $tmpnode.height();
					$canvas[0].appendChild(el);
					graph_renderer.canvasNode = el;
				}
				graph_renderer.drawer = new CanvasDrawer(graph_renderer.canvasNode);
				graph_renderer.needCalRange = true;
				graph_renderer.canvasPos = graph_renderer.canvasNode.getBoundingClientRect();
			}else if(document.namespaces) {
				if(!document.namespaces.v){
					document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', '#default#VML');
				}
				graph_renderer.drawer = new VmlDrawer(graph_renderer.canvasNode);
			}else{
				//TODO SVG
				graph_renderer.drawer = new BaseDrawer();
			}
			
			if(!disEvents){
				graph_renderer.drawer.delegateEvents();
			}
			graph_renderer.inited = true;
		}
	};

	XFrame_Graph.drawDefiner = XFrame_Graph._createClass({
		init : function(){
			this.commands = {'default' : []};
			this.nowType = 'default';
			this.resetStyle();
		},
		changeEditType : function(t){
			t = t || 'default';
			this.nowType = t;
			if(!this.commands[t]){
				this.commands[t] = [];
			}
			this.resetStyle();
		}, 
		draw : function(s){
			this.commands[this.nowType].push({
				method : 'draw' + s,
				params : Array.prototype.slice.call(arguments, 1)
			});
		},
		style : function(s_f){
			this.commands[this.nowType].push({
				method : 'set' + s_f,
				params : Array.prototype.slice.call(arguments, 1)
			});
		},
		resetStyle : function(s_f){
			this.commands[this.nowType].push({
				method : s_f ? ('set' + s_f) : 'resetStyle',
				params : []
			});
		},
		getCommands : function(){
			var cmds = {};
			$.each(this.commands, function(key, cmd){
				cmds[key] = cmd.slice();
			});
			return cmds;
		}
	});
	
	XFrame_Graph.setGraphScale = function(sv){
		graph_renderer.setScale(sv);
	};
	
	

	RenderStructure = XFrame_Graph._createClass({
		init : function(baseShape){
			this.id = baseShape.uuid;
			this.subList = null;
			this.next = null;
			this.zindex = baseShape.zindex;
			this.commands = baseShape.drawDefine() || [];
			this.selfShape = baseShape;
			this.rangeCmds = null;
			this.status = 0;
			
			if(graph_renderer.needCalRange && !baseShape.disableEvents){
				this.rangeCmds = baseShape.rangeDefine();
				if(!this.rangeCmds){
					this.rangeCmds = [];
					this.rangeCmds.push({
						method : 'testRect',
						params : 'rectShape'
					});
				}
			}
			
		},
		linkPre : function(preRs){
			this.next = preRs.next;
			preRs.next = this.id;
		},
		reGenCommands : function(){
			this.commands = this.selfShape.drawDefine() || [];
		}
	});


	RenderLinkedList = XFrame_Graph._createClass({
		init : function(){
			this.__innerObj__ = {};
			this.__indexObj__ = {};
			this.__zarr__ = [];
			this.startId = null;
			this.endId = null;
			this.length = 0;
		},
		add : function(elem){
			var zi = elem.zindex
				,rs = new RenderStructure(elem)
				, prezi, prers = null;
			if(!this.__indexObj__[zi]){
				this.__indexObj__[zi] = [];
				prezi = findPreZi(zi, this.__zarr__);
				this.__zarr__.push(zi);
				this.__zarr__.sort();

			}else{
				prezi = zi;
			}
			prezi !== null && (prers = this.__indexObj__[prezi][this.__indexObj__[prezi].length - 1]);
			if(prers){
				rs.linkPre(prers);
			}else{
				rs.next = this.startId;
				this.startId = rs.id;
			}
			if(!rs.next){
				this.endId = rs.id;
			}
			this.__indexObj__[zi].push(rs);
			this.__innerObj__[rs.id] = rs;
			this.length++;

			return rs;
		},
		get : function(index_or_id){
			var nowrs;
			if(typeof index_or_id === 'string'){
				return this.__innerObj__[index_or_id];
			}else if(typeof index_or_id === 'number'){
				nowrs = this.__innerObj__[this.startId];
				while(index_or_id--){
					nowrs && (nowrs = this.__innerObj__[nowrs.next]);
				}
				return nowrs;
			}
			return null;
		},
		toArray : function(){
			var tmparr = [], nowrs;
			var nowid = this.startId;
			while(nowid){
				nowrs = this.__innerObj__[nowid];
				tmparr.push(nowrs);
				nowid = nowrs.next;
			}
			return tmparr;
		},
		remove : function(elem_or_id){
			var thiselem, thisid, zi, idxarr, thisidx, preelem, preidx;
			if(typeof elem_or_id === 'string'){
				thisid = elem_or_id;
				thiselem = this.__innerObj__[thisid];
			}else if(typeof elem_or_id === 'object'){
				thisid = elem_or_id.id;
				thiselem = elem_or_id;
			}
			if(!thisid || !thiselem){
				return false;
			}
			if(thisid === this.startId){
				this.startId = thiselem.next;
			}
			zi = thiselem.zindex;
			idxarr = this.__indexObj__[zi];
			thisidx = $.inArray(thiselem, idxarr);
			if(thisidx){
				preelem = idxarr[thisidx - 1];
			}else{
				preidx = this.__zarr__[$.inArray(zi, this.__zarr__) - 1];
				preelem =  this.__indexObj__[preidx][this.__indexObj__[preidx].length - 1];
			}
			if(preelem){
				preelem.next = thiselem.next;
				if(!preelem.next){
					this.endId = preelem.id;
				}
			}
			idxarr.splice(thisidx, 1);
			if(idxarr.length === 0){
				delete this.__indexObj__[zi];
				this.__zarr__.splice($.inArray(zi, this.__zarr__), 1);
			}
			delete this.__innerObj__[thisid];
			this.length--;
			return true;
		},
		foreach : function(startPos, fn, scope){
			var nowElem;
			startPos = startPos || this.startId;
			nowElem = this.get(startPos);
			while(nowElem){
				if(fn.call(scope || nowElem, nowElem) === false){
					nowElem = null;
				}else{
					nowElem = this.__innerObj__[nowElem.next];
				}
			}
		}
	});

	graph_renderer = {
		inited : false,
		canvasNode : null,
		needCalRange : false,
		canvasPos : null,
		width : 0,
		heigth : 0,
		allShapes : {},
		mainList : new RenderLinkedList(),
		allRenderStructure : {},
		drawer : null,
		renderTryTimes : 0,
		render : function(refresh, force){
			if(imageCacher.checkStatus() || force){
				if(refresh){
					this.clear();
				}
				this.drawer.renderList(this.mainList);
			}else{
				this.renderTryTimes++;
				if(this.renderTryTimes < 20){
					setTimeout(function(){
						graph_renderer.render(refresh);
					}, 100);
				}else{
					this.render(refresh, true);
				}
			}
		},
		clear : function(){
			this.drawer.clearAll();
		},
		findRootRs : function(rs){
			var rrs = rs, parentId;
			while(!rrs.selfShape.canProcessRootEvent &&  (parentId = rrs.selfShape.zcontext)){
				rrs = this.allRenderStructure[parentId];
			}
			return rrs;
		},
		nowFocusRs : null,
		isDragMode : false,
		dragInfo : null,
		scaleValue : 1,
		setScale : function(newScale){
			this.scaleValue = this.drawer.changeScale(newScale);
			this.render(true);
		}
	};
	
	XFrame_Graph.clear = $.proxy(graph_renderer.clear, graph_renderer);
	window.xg = XFrame_Graph;

	findPreZi = function(nowZi, zarr){
		
		var s = zarr.legnth;
		if(!s){
			return null;
		}
		if(nowZi < zarr[0]){
			return null;
		}
		if(nowZi > zarr[s - 1]){
			return zarr[s - 1];
		}
		return findPreZiHelper(nowZi, zarr);
	};

	findPreZiHelper = function(nowZi, zarr){
		if(zarr.length === 0){
			return null;
		}
		var mididx = ((zarr.length / 2) | 0) - 1;
		if(nowZi > zarr[mididx]){
			if(nowZi < zarr[mididx + 1]){
				return zarr[mididx];
			}else{
				return findPreZiHelper(nowZi, zarr.slice(mididx + 1));
			}
		}else{
			return findPreZiHelper(nowZi, zarr.slice(0, mididx));
		}
	};
	
	checkStatus = function(s, b){
		return (s >> b) & 1;
	};
	
	setStatus = function(s, b, v){
		if(v){
			return s | (1 << b);
		}else{
			return s & ((1 << b) - 1);
		}
	};

	_getMouseSpPosition = function(shape, mx, my){
		var sl = shape.getAbsolutePosX(), sr = shape.getAbsolutePosX() + shape.width,
			st = shape.getAbsolutePosY(), sb = shape.getAbsolutePosY() + shape.height, res = null;

		if(my - st < 5){
			res = (res || '') + 't';
		}
		if(sb - my < 5){
			res = (res || '') + 'b';
		}
		if(mx - sl < 5){
			res = (res || '') + 'l';
		}
		if(sr - mx < 5){
			res = (res || '') + 'r';
		}
			
		return res ? res.replace(/tb|lr/g, function(s){return s.substring(1);}) : null;
	};
	
	_EF = function(){};
	
	BaseDrawer = XFrame_Graph._createClass({
		init : _EF,
		setShadow : _EF,
		setStroke : _EF,
		setFill : _EF,
		setAlpha : _EF,
		setText : _EF,
		setScale : _EF,
		setLineWidth : _EF,
		resetStyle : _EF,
		drawLine : _EF,
		drawArc : _EF,
		drawImage : _EF,
		drawDashedLine : _EF,
		drawShape : _EF,
		drawRect : _EF,
		drawFilletRect : _EF,
		drawHalfFilletRect : _EF,
		renderList : _EF,
		delegateEvents : _EF,
		clearAll : _EF,
		changeScale : _EF,
		changeParentShape : _EF
	});

	CanvasDrawer = BaseDrawer.extend({
		init : function(cnode){
			
			this.canvasNode = cnode;
			this.canvasWidth = cnode.width;
			this.canvasHeight = cnode.height;
			this.width = cnode.width;
			this.height = cnode.height;
			this.context = cnode.getContext('2d');
			this.context.save();
			this.nowScale = 1;
			
			this.fill = null;
			this.stroke = null;
			this.alpha = 1;
			this.drawed = false;
			this.sortedRss = [];
		},
		changeScale : function(newv){
			if(!$.isNumeric(newv) || +newv <= 0){
				return this.nowScale;
			}
			newv = +newv;
			this.context.restore();
			this.context.save();
			this.context.scale(newv, newv);
			this.width = this.canvasWidth / newv;
			this.height = this.canvasHeight / newv;
			return this.nowScale = newv;
		},
		
		setScale : function(svalue){
		    if(!$.isNumeric(svalue) || +svalue <= 0){
		        return;
		    }
		    this.context.scale(svalue, svalue);
		    
		},
		setText : function(font, textAlign, textBaseline){
			font && (this.context.font = font);
			textAlign && (this.context.textAlign = textAlign);
			textBaseline && (this.context.textBaseline = textBaseline);
		},
		setAlpha : function(a){
		    if(!$.isNumeric(a)){
		        return;
		    }
		    a = +a;
		    if(a < 0 || a > 1){
		        return;
		    }
		    this.alpha = a;
		    this.fill = _parseColor2RGBA(this.fill, a);
		    this.stroke = _parseColor2RGBA(this.stroke, a);
		},
		setStroke : function(s){
		    if(this.alpha === 1){
                this.stroke = s;
            }else{
                this.stroke = _parseColor2RGBA(s, this.alpha);
            }
			
			if(!s){
				return;
			}
			this.context.strokeStyle = this.stroke;
		},
		setFill : function(f){
		    if(this.alpha === 1){
		        this.fill = f;
		    }else{
		        this.fill = _parseColor2RGBA(f, this.alpha);
		    }
			
			if(!f){
				return;
			}
			if(typeof f === 'string'){
				this.context.fillStyle = this.fill;
			}else if(f.creator && f.stops){
				var grd=this.context.createLinearGradient(f.creator[0],f.creator[1],f.creator[2],f.creator[3]);
				$.each(f.stops, function(idx, cs){
					grd.addColorStop(cs[0], cs[1]);
				});
				this.context.fillStyle=grd;
			}
			
		},
		setLineWidth : function(w){
			w = +w;
			if(w > 0){
				this.context.lineWidth = w;
			}
		},
		setShadow : function(color, blur){
			this.context.shadowColor = color || 'white';
			this.context.shadowBlur = blur || 0;
		},
		resetStyle : function(){
			this.fill = null;
			this.stroke = null;
			this.alpha = 1;
			this.setShadow('#000000', 0);
		},
		drawText : function(txt, x, y , maxWidth, fill){
			if(fill){
				this.context.fillText(txt, x, y , maxWidth);
			}else{
				this.context.strokeText(txt, x, y , maxWidth);
			}
		},
		drawImage : function(imgUrl){
			var img = imageCacher.getImg(imgUrl),args;
			if(img){
				args = Array.prototype.slice.call(arguments, 1);
				args.unshift(img.img);
				this.context.drawImage.apply(this.context, args);
			}
		},
		drawRect : function(x, y, width, height){
			this.context.beginPath();
			this.context.moveTo(x, y);
			this.context.lineTo(x + width, y);
			this.context.lineTo(x + width, y + height);
			this.context.lineTo(x, y + height);
			this.context.closePath();
			if(this.stroke){
				this.context.stroke();
			}
			if(this.fill){
				this.context.fill();
			}
		},
		drawFilletRect : function(x, y, width, height, r){
			if(r > width/2 || r > height/2){
				r = Math.min(width/2, height/2) | 0;
			}
			this.context.beginPath();
			this.context.moveTo(x, y + height - r);
			this.context.lineTo(x, y + r);
			this.context.arcTo(x, y, x + r, y, r);
			this.context.lineTo(x + width - r, y);
			this.context.arcTo(x + width, y, x + width, y + r, r);
			this.context.lineTo(x + width, y + height - r);
			this.context.arcTo(x + width, y + height, x + width - r, y + height, r);
			this.context.lineTo(x + r, y + height);
			this.context.arcTo(x, y + height, x, y + height - r, r);

			if(this.stroke){
				this.context.stroke();
			}
			if(this.fill){
				this.context.fill();
			}
		},
		drawLine : function(x1, y1, x2, y2){
			this.context.beginPath();
			this.context.moveTo(x1, y1);
			this.context.lineTo(x2, y2);
			this.context.stroke();
		},
		drawHalfFilletRect : function(x1, y1, x2, y2, r, yFirst){
			var rx, ry;
			this.context.beginPath();
			this.context.moveTo(x1, y1);
			if(y1 === y2 || x1 === x2){
				this.context.lineTo(x2 , y2);
			}else{
				if(yFirst){
					r = Math.abs(y1 - y2) <= r ? 0 : r;
					rx = x2 > x1 ? r : -r;
					ry = y2 > y1 ? r : -r;
					this.context.lineTo(x1, y2 - ry);
					r && this.context.arcTo(x1, y2, x1 + rx, y2, r);
					this.context.lineTo(x2, y2);
				}else{
					r = Math.abs(x1 - x2) <= r ? 0 : r;
					rx = x2 > x1 ? r : -r;
					ry = y2 > y1 ? r : -r;
					this.context.lineTo(x2 - rx, y1);
					r && this.context.arcTo(x2, y1, x2, y1 + ry, r);
					this.context.lineTo(x2, y2);
				}
			}
			if(this.stroke){
				this.context.stroke();
			}
		},
		testRect : function(x, y, width, height){
			
			this.context.beginPath();
			this.context.rect(x, y, width, height);
		},
		renderList : function(rList, parentRs){
			if(!rList){
				return;
			}
			var activeRs = null, frontRss = [];
			if(!parentRs){
				this.sortedRss.length = 0;
			}
			
			rList.foreach(0, function(rs){
				if(rs.selfShape.isHidden){
				    return true;
				}
				if(rs.selfShape.isActive || rs.selfShape.isAlwaysOnTop){
					if(parentRs){
						rs[':parentRs'] = parentRs;
					}
					if(rs.selfShape.isActive){
						activeRs = rs;
					}else{
						frontRss.push(rs);
					}
					return true;
				}
				
				this.sortedRss.push(rs);
				this.context.save();
				if(parentRs){
					this.context.translate(parentRs.selfShape.posX, parentRs.selfShape.posY);
				}
				$.each(rs.commands[rs.selfShape.status] || rs.commands['default'],$.proxy(function(idx, cmd){
					this[cmd.method].apply(this, cmd.params);
				}, this));
				rs.subList && this.renderList(rs.subList, rs);
				this.context.restore();
			}, this);
			
			if(activeRs){
				if(activeRs.selfShape.isAlwaysOnTop){
					frontRss.push(activeRs);
				}else{
					frontRss.unshift(activeRs);
				}
			}
			$.each(frontRss, $.proxy(function(idx, rs){
				this.sortedRss.push(rs);
				
				var parentRs = rs[':parentRs'];
				this.context.save();
				if(parentRs){
					this.context.translate(parentRs.selfShape.posX, parentRs.selfShape.posY);
					delete rs[':parentRs'];
				}
				$.each(rs.commands[rs.selfShape.status] || rs.commands['default'],$.proxy(function(idx, cmd){
					this[cmd.method].apply(this, cmd.params);
				}, this));
				rs.subList && this.renderList(rs.subList, rs);
				this.context.restore();
			}, this));
			
			this.drawed = true;
		},
		delegateEvents : function(){
			var $canvas = $(this.canvasNode);
			$canvas.mousedown(function(e){
				var _this = graph_renderer.drawer, $this = $(this);
				if(!_this.drawed){
					return;
				}
				var evt = $.event.fix(e)
					, mx = (evt.pageX - graph_renderer.canvasPos.left) 
					, my = (evt.pageY - graph_renderer.canvasPos.top) 
					, sps = _this.getEventShapes(mx, my), frontRs, frontRootRs;
					evt.mx = mx;
					evt.my = my;
					
				if(sps[0].length){
					frontRs = sps[0][0];
					frontRootRs = graph_renderer.findRootRs(frontRs);
					frontRootRs.selfShape.triggerEvent('mousedown', evt);
					if(graph_renderer.nowFocusRs !== frontRootRs){
						graph_renderer.nowFocusRs && graph_renderer.nowFocusRs.selfShape.triggerEvent('inactive', evt);
						if(frontRootRs !== frontRs){
							if(!frontRootRs.selfShape.isActive){
								frontRootRs.selfShape.triggerEvent('active', evt);
							}
						}
						if(!frontRootRs.selfShape.isActive){
							frontRootRs.selfShape.triggerEvent('active', evt);
						}
						
						graph_renderer.nowFocusRs = frontRootRs;
					}
					if(frontRootRs.selfShape.dragable || frontRootRs.selfShape.resizable){
						frontRootRs.selfShape.triggerEvent('dragstart', evt);
						graph_renderer.isDragMode = true;
						graph_renderer.dragInfo = [mx ,frontRootRs.selfShape.posX, my ,frontRootRs.selfShape.posY, 
													_this.width - graph_renderer.nowFocusRs.selfShape.width, _this.height - graph_renderer.nowFocusRs.selfShape.height, 
													graph_renderer.nowFocusRs.selfShape.width, graph_renderer.nowFocusRs.selfShape.height];
						if(_this.canvasNode.setCapture){
	                    	_this.canvasNode.setCapture(true);
	                    	$this.unbind('mousemove').bind('mousemove', _this.__dragHandler);
	                    	$this.bind('mouseup', _this.__dragEnd);
	                    	$this.bind('losecapture', _this.__dragEnd);
	                    }else{
	                    	window.captureEvents && window.captureEvents(Event.MOUSEMOVE | Event.MOUSEUP);
	                    	$(document).bind('mousemove', _this.__dragHandler).bind('mouseup', _this.__dragEnd);
	                    	//window.captureEvents(Event.MOUSEMOVE | Event.MOUSEUP);
	                    	window.onblur = _this.__dragEnd;
	                    }
					}else{
						$this.unbind('mouseup').bind('mouseup', _this.__normalMouseupHandler);
					}
				}else{
					graph_renderer.nowFocusRs && graph_renderer.nowFocusRs.selfShape.triggerEvent('inactive', evt, true);
					graph_renderer.nowFocusRs = null;
				}
			})
			.mousemove(this.__moveHandler)
			.mouseout(function(e){
				
			});
		},
		__moveHandler : function(e){
			if(graph_renderer.isDragMode){
				return;
			}
			var _this = graph_renderer.drawer;
			if(!_this.drawed){
				return;
			}
			var evt = $.event.fix(e)
				, mx = (evt.pageX - graph_renderer.canvasPos.left) 
				, my = (evt.pageY - graph_renderer.canvasPos.top) 
				, sps = _this.getEventShapes(mx, my), needSetDefaultCursor = true;
				evt.mx = mx;
				evt.my = my;

			
			//this.canvasNode.style.cursor = 'default';
			$.each(sps[0], $.proxy(function(idx, ss){
				var thisShape = ss.selfShape, spd = _getMouseSpPosition(thisShape, mx, my);
				if(!checkStatus(ss.status, 0)){
					ss.status = setStatus(ss.status, 0, 1);
					thisShape.triggerEvent('mousein', evt);

				}
				if(thisShape.resizable && spd){
					thisShape.spDragMode = spd;
					needSetDefaultCursor = false;

					switch(spd){
						case 't' : 
							_this.canvasNode.style.cursor = 'ns-resize';
							break;
						case 'b' : 
							_this.canvasNode.style.cursor = 'ns-resize';
							break;
						case 'l' :
							_this.canvasNode.style.cursor = 'ew-resize';
							break;
						case 'r' :
							_this.canvasNode.style.cursor = 'ew-resize';
							break;
						case 'tl':
							_this.canvasNode.style.cursor = 'nwse-resize';
							break;
						case 'tr':
							_this.canvasNode.style.cursor = 'nesw-resize';
							break;
						case 'bl':
							_this.canvasNode.style.cursor = 'nesw-resize';
							break;
						case 'br':
							_this.canvasNode.style.cursor = 'nwse-resize';
							break;
					}
				}else{
					thisShape.spDragMode = null;
				}
				

				return false;

			}, this));
			
			

			$.each(sps[1], function(){
				var thisShape = this.selfShape;
				if(checkStatus(this.status, 0)){
					this.status = setStatus(this.status, 0, 0);

					thisShape.spDragMode = null;
					return thisShape.triggerEvent('mouseout', evt);
				}
			});

			if(needSetDefaultCursor){
				_this.canvasNode.style.cursor = 'default';
			}
		},
		__dragHandler : function(e){
			var _this = graph_renderer.drawer, draggingShape = graph_renderer.nowFocusRs.selfShape
				, evt = $.event.fix(e)
				, mx = (evt.pageX - graph_renderer.canvasPos.left) 
				, my = (evt.pageY - graph_renderer.canvasPos.top) 
				;
				
			evt.mx = mx;
			evt.my = my;
			draggingShape.triggerEvent('dragging', evt, graph_renderer.dragInfo, _this.nowScale);
			draggingShape.dragged = true;

			e && e.preventDefault && e.preventDefault();
        	return false;
		},
		__dragEnd : function(e){
			var _this = graph_renderer.drawer, $this = $(this)
				, draggingShape = graph_renderer.nowFocusRs.selfShape, evt = $.event.fix(e);
			if(_this.canvasNode.releaseCapture){
	        	_this.canvasNode.releaseCapture(true);
	        	$this.unbind('mousemove').bind('mousemove', _this.__moveHandler);
	        	$this.unbind('mouseup');
	        	$this.unbind('losecapture');
	        }else{
	        	$(document).unbind('mousemove').unbind('mouseup');
	        	window.releaseEvents && window.releaseEvents(Event.MOUSEMOVE | Event.MOUSEUP);
	        	window.onblur = null;
	        }
	        graph_renderer.isDragMode = false;
			graph_renderer.dragInfo = null;
			draggingShape.triggerEvent('dragend', evt);
			draggingShape.triggerEvent('mouseup', evt);
			if(!draggingShape.dragged){
				draggingShape.triggerEvent('click', evt);
			}
			
			e && e.preventDefault && e.preventDefault();
        	return false;
		},
		__normalMouseupHandler : function(e){
			var draggingShape = graph_renderer.nowFocusRs && graph_renderer.nowFocusRs.selfShape, evt = $.event.fix(e), _this = graph_renderer.drawer;
			$(_this).unbind('mouseup');
			if(!draggingShape){
				return;	
			}
			draggingShape.triggerEvent('mouseup', evt);
			draggingShape.triggerEvent('click', evt);
		},
		getEventShapes : function(mx, my){
			var es = [], unes = [], res = [];
			this.context.save();
			$.each(this.sortedRss, $.proxy(function(idx, rs){
				
				if(rs.rangeCmds){
					this.context.beginPath();
					$.each(rs.rangeCmds, $.proxy(function(idx, cmd){
						if(cmd.params === 'rectShape'){
							this[cmd.method].apply(this, [rs.selfShape.getAbsolutePosX(), rs.selfShape.getAbsolutePosY(), rs.selfShape.width, rs.selfShape.height]);
						}else{
							this[cmd.method].apply(this, cmd.params);
						}
					}, this));
					if(this.context.isPointInPath(mx, my)){
						es.unshift(rs);
					}else{
						unes.push(rs);
					}
				}

			}, this));
			this.context.restore();
			res.push(es);
			res.push(unes);
			return res;
		},
		clearAll : function(){
			this.context.clearRect(0, 0, this.width, this.height);
		}
	});
	
	VmlDrawer = BaseDrawer.extend({
		init : function(cnode){
			this.vmlcontainer = cnode;
			this.$vmlcontainer = $(cnode);
			this.width = this.$vmlcontainer.width();
			this.height = this.$vmlcontainer.height();
			this.pixelWidth = this.width;
			this.pixelHeight = this.height;
			this.nowScale = 1;
			
			this.parentShape = null;
			this.thisShapeId = null;
			this.baseX = 0;
			this.baseY = 0;
			this.nowZindex = 0;
			this.genShapes = '';
			
			this.fill = null;
			this.stroke = null;
			this.alpha = 1;
			this.shadow = null;
			this.shadowStrenth = 0;
			this.font = null;
			this.textAlign = 'left';
			this.lineWidth = 1;
			this.textBaseline = 'top';
			this.vmlcontainer.insertAdjacentHTML('beforeEnd', '<v:group coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '"' +
                    ' style="position:absolute;top:0;left:0;width:' + this.pixelWidth + 'px;height=' + this.pixelHeight + 'px;"></v:group>');
            this.$maingroup = this.$vmlcontainer.children(':last')
//            						.append('<v:group coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '"' +
//                    					' style="position:absolute;top:0;left:0;width:' + this.pixelWidth + 'px;height=' + this.pixelHeight + 'px;"></v:group>').children(':first')
                    		;
		},
		changeScale : function(newv){
			if($.isNumeric(newv) && +newv > 0){
				this.nowScale = +newv;
				this.width = (this.pixelWidth / this.nowScale) |0;
				this.height = (this.pixelHeight / this.nowScale) |0;
				
				graph_renderer.render(true);
			}
			
		},
		changeParentShape : function(shape){
			this.parentShape = shape;
			if(shape){
				this.baseX = shape.getAbsolutePosX();
				this.baseY = shape.getAbsolutePosY();
			}else{
				this.baseX = 0;
				this.baseY = 0;
			}
		},
		setShapeId : function(uuid){
			this.thisShapeId = uuid;
		},
		setText : function(font, textAlign, textBaseline){
			this.font = font;
			textAlign && (this.textAlign = textAlign.toLowerCase());
			textBaseline && (this.textBaseline = textBaseline.toLowerCase());
		},
		setStroke : function(s){
			this.stroke = s;
		},
		setFill : function(f){
			this.fill = f;
		},
		setShadow : function(color, blur){
			this.shadow = color;
			this.shadowStrenth = blur;
		},
		resetStyle : function(){
			this.fill = null;
			this.stroke = null;
			this.alpha = 1;
			this.shadow = null;
			this.font = null;
			this.textAlign = 'left';
		},
		drawText : function(txt, x, y){
			if(!this.font){
				return '';
			}
			var vel, px1, py1, px2, py2, stroke = '', fill = '', alphaStyle = '', fontsize, tmpfont;
			tmpfont = this.font.replace(/\d+/, $.proxy(function(s){
				var fs = +s;
				fs = (this.nowScale * fs) | 0;
				return fs;
			}, this));
			fontsize = +(/\d+/.exec(tmpfont) || '9');
			
			x += this.baseX;
			y += this.baseY;
			if(this.textBaseline === 'top' || this.textBaseline === 'hanging' ){
				y = y + ((fontsize / 2) | 0);
			}else if (this.textBaseline === 'bottom' || this.textBaseline === 'ideographic'  || this.textBaseline === 'alphabetic' ){
				y = y - ((fontsize / 2) | 0);
			}
			if(this.stroke){
				stroke = ' strokeWeight="' + this.lineWidth + 'px" strokeColor="' + this.stroke + '" ';
			}
			if(this.fill){
        		fill = ' fillColor="' + this.fill + '" filled="true" ';
			}
			if(this.alpha >= 0 && this.alpha < 1){
	        	alphaStyle = "filter:Alpha(Opacity=" + (this.alpha * 100) + ");";
	        }
			
			if(this.textAlign === 'left'){
				px1 = x;
				py2 = py1 = y;
				px2 = x + 10;
			}else if(this.textAlign === 'right'){
				px1 = x - 10;
				py2 = py1 = y;
				px2 = x;
			}else {
				py2 = py1 = y;
				px1 = x - 10;
				px2 = x + 10;
			}
			vel = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '" ' +
                 ' class="' + this.thisShapeId + '" ' +	
                 stroke +
                 fill +
                ' style="z-index:' + this.nowZindex + ';position:absolute;left:0px;top:0px;height:' + this.pixelHeight + 'px;width:' + this.pixelWidth + 'px;padding:0px;margin:0px;' + alphaStyle + '" ' +
                ' path="m ' + px1 + ',' + py1 + ' l ' + px2 + ',' + py2 + ' ' + 'e">' +
                '<v:path textpathok="true"/>' +
                '<v:fill on="True"/>' +
                '<v:textpath on="true" string="' + txt + '" style="font:' + tmpfont + '; v-text-align:' + this.textAlign + ';"/>' +
                ' </v:shape>';
                
            return vel;
		},
		drawImage : function(imgUrl){
			var vel, 
				thisImg = imageCacher.getImg(imgUrl).img, 
				thisImgHeight = thisImg.height,
				thisImgWidth = thisImg.width,
				thisImgSrc=  thisImg.src,
				argsLength = arguments.length, cutInfo = '',
				iWidth = thisImgWidth,
				iHeight = thisImgHeight,
				px = 0, py = 0,
				alphaStyle = '';

			if(this.alpha >= 0 && this.alpha < 1){
        		alphaStyle = "filter:Alpha(Opacity=" + (this.alpha * 100) + ");";
	        }
			if(argsLength === 5){
				iWidth = arguments[3];
				iHeight = arguments[4];
				px = arguments[1];
				py = arguments[2];
			}else if (argsLength === 9){
				iWidth = arguments[7];
				iHeight = arguments[8];
				px = arguments[5];
				py = arguments[6];
				cutInfo = 'cropleft="' + ((arguments[1] * 100 / thisImgWidth) | 0) / 100 + '" ' 
							+ 'croptop="' + ((arguments[2] * 100 / thisImgHeight) | 0) / 100 + '" ' 
							+ 'cropright="' + (((thisImgWidth - arguments[1]  - arguments[3]) * 100 / thisImgWidth) | 0) / 100 + '" ' 
							+ 'cropbottom="' + (((thisImgHeight - arguments[2]  - arguments[4]) * 100 / thisImgHeight) | 0) / 100 + '" ' ;
						
			}else{
				px = arguments[1];
				py = arguments[2];
			}
			
			px += this.baseX;
			py += this.baseY;
			
			vel = '<v:image ' + cutInfo + ' ' +
					' class="' + this.thisShapeId + '" ' +	
					'src="' + thisImgSrc + '" ' +
							'style="z-index:' + this.nowZindex + ';LEFT:' + px + ';TOP:' + py + ';WIDTH:' + iWidth + ';HEIGHT:' + iHeight + ';POSITION:absolute;' + alphaStyle + '"/>';
			
			return vel;
		},
		
		drawRect : function(x, y, width, height){
			var vel, stroke = '', fill = '', initial, vpath, alphaStyle = '',shadowStyle = '';
			x += this.baseX;
			y += this.baseY;
			
			x = x | 0;
			y = y | 0;
			width = width | 0;
			height = height | 0;
			
			initial = [x, y];
			vpath = [x + width, y , x + width, y + height, x, y + height];
			
			if(this.stroke){
				stroke = ' strokeWeight="' + this.lineWidth + 'px" strokeColor="' + this.stroke + '" ';
			}
			if(this.fill){
        		fill = ' fillColor="' + this.fill + '" filled="true" ';
			}
			if(this.shadow){
				shadowStyle = 'filter: Glow(color=' + this.shadow + ',strength=' + this.shadowStrenth + ');';
			}
			
			if(this.alpha >= 0 && this.alpha < 1){
	        	alphaStyle = "filter:Alpha(Opacity=" + (this.alpha * 100) + ");";
	        }
			
			vel = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '" ' +
                 ' class="' + this.thisShapeId + '"  ' +
                 stroke +
                 fill +
                ' style="' + shadowStyle + 'z-index:' + this.nowZindex + ';position:absolute;left:0px;top:0px;height:' + this.pixelHeight + 'px;width:' + this.pixelWidth + 'px;padding:0px;margin:0px;' + alphaStyle + '" ' +
                ' path="m ' + initial + ' l ' + vpath.join(', ') + ' ' + 'x e">' +
                
                ' </v:shape>';
			
			return vel;
		},
		
		drawFilletRect : function(x, y, width, height, r){
			var vel, stroke = '', fill = '', initial, vpath, alphaStyle = '', shadowStyle = '';
			x += this.baseX;
			y += this.baseY;
			
			if(r > width/2 || r > height/2){
				r = Math.min(width/2, height/2) | 0;
			}
			
			x = x | 0;
			y = y | 0;
			width = width | 0;
			height = height | 0;
			r = r | 0;
			
			initial = [x + r, y];
			vpath = 'l' + [x + width -r, y].join() 
					+ 'qx' + [x + width, y + r].join() 
					+ 'l' + [x + width, y + height - r].join()
					+ 'qy' + [x + width -r, y + height].join()
					+ 'l' + [x + r, y + height].join()
					+ 'qx' + [x, y + height - r].join()
					+ 'l' + [x , y + r].join()
					+ 'qy' + [x + r, y].join();
			
			if(this.stroke){
				stroke = ' strokeWeight="' + this.lineWidth + 'px" strokeColor="' + this.stroke + '" ';
			}
			if(this.fill){
        		fill = ' fillColor="' + this.fill + '" filled="true" ';
			}
			
			if(this.shadow){
				shadowStyle = 'filter: Glow(color=' + this.shadow + ',strength=' + this.shadowStrenth + ');';
			}
			
			if(this.alpha >= 0 && this.alpha < 1){
	        	alphaStyle = "filter:Alpha(Opacity=" + (this.alpha * 100) + ");";
	        }
			
			vel = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '" ' +
                 ' class="' + this.thisShapeId + '"  ' +
                 stroke +
                 fill +
                ' style="' + shadowStyle + 'z-index:' + this.nowZindex + ';position:absolute;left:0px;top:0px;height:' + this.pixelHeight + 'px;width:' + this.pixelWidth + 'px;padding:0px;margin:0px;' + alphaStyle + '" ' +
                ' path="m ' + initial + ' ' + vpath + ' ' + 'e">' +
                
                ' </v:shape>';
			
			return vel;
		},
		drawHalfFilletRect : function(x1, y1, x2, y2, r, yFirst){
			var vel, stroke = '', fill = '', initial, vpath, alphaStyle = '', rx, ry, shadowStyle = '';
			r = Math.min(Math.abs(x1 - x2), Math.abs(y1 - y2)) < r ? 0 : r;
			
			x1 = x1 | 0;
			y1 = y1 | 0;
			x2 = x2 | 0;
			y2 = y2 | 0;;
			r = r | 0;
			
			initial = [x1, y1];
			if(yFirst){
				rx = x2 > x1 ? +r : -r;
				ry = y2 > y1 ? +r : -r;
				vpath = 'l' + [x1, y2 - ry].join()
						+ 'qy' + [x1 + rx, y2].join()
						+ 'l' + [x2, y2].join();
			}else{
				rx = x2 > x1 ? r : -r;
				ry = y2 > y1 ? r : -r;
				vpath = 'l' + [x2 - rx, y1].join()
						+ 'qx' + [x2, y1 + ry].join()
						+ 'l' + [x2, y2].join();
			}
			
			if(this.stroke){
				stroke = ' strokeWeight="' + this.lineWidth + 'px" strokeColor="' + this.stroke + '" ';
			}
			if(this.fill){
        		fill = ' fillColor="' + this.fill + '" filled="true" ';
			}
			if(this.shadow){
				shadowStyle = 'filter: Glow(color=' + this.shadow + ',strength=' + this.shadowStrenth + ');';
			}
			if(this.alpha >= 0 && this.alpha < 1){
	        	alphaStyle = "filter:Alpha(Opacity=" + (this.alpha * 100) + ");";
	        }
	        
	        vel = '<v:shape coordorigin="0 0" coordsize="' + this.pixelWidth + ' ' + this.pixelHeight + '" ' +
                 ' class="' + this.thisShapeId + '" ' +	
                 stroke +
                 fill +
                ' style="' + shadowStyle + 'z-index:' + this.nowZindex + ';position:absolute;left:0px;top:0px;height:' + this.pixelHeight + 'px;width:' + this.pixelWidth + 'px;padding:0px;margin:0px;' + alphaStyle + '" ' +
                ' path="m ' + initial + ' ' + vpath + ' ' + 'e">' +
                
                ' </v:shape>';
			
			return vel;
		},
		renderList : function(rList, parentRs){
			if(!rList){
				return;
			}
			var activeRs = null, frontRss = [];
			if(!parentRs){
				this.nowZindex = 0;
				this.genShapes = '';
			}
			
			rList.foreach(0, function(rs){
				if(rs.selfShape.isHidden){
                    return true;
                }
				if(rs.selfShape.isActive || rs.selfShape.isAlwaysOnTop){
					if(parentRs){
						rs[':parentRs'] = parentRs;
					}
					if(rs.selfShape.isActive){
						activeRs = rs;
					}else{
						frontRss.push(rs);
					}
					return true;
				}
				this.nowZindex++;
				this.changeParentShape(parentRs ? parentRs.selfShape : parentRs);
				this.setShapeId(rs.id);
				
				$.each(rs.commands[rs.selfShape.status] || rs.commands['default'],$.proxy(function(idx, cmd){
					this.genShapes = this.genShapes + (this[cmd.method].apply(this, cmd.params) || '');
				}, this));
				rs.subList && this.renderList(rs.subList, rs);
				
			}, this);
			
			if(activeRs){
				if(activeRs.selfShape.isAlwaysOnTop){
					frontRss.push(activeRs);
				}else{
					frontRss.unshift(activeRs);
				}
			}
			$.each(frontRss, $.proxy(function(idx, rs){
				
				var parentRs = rs[':parentRs'];
				
				if(parentRs){
					delete rs[':parentRs'];
				}
				this.changeParentShape(parentRs ? parentRs.selfShape : parentRs);
				this.setShapeId(rs.id);
				this.nowZindex++;
				$.each(rs.commands[rs.selfShape.status] || rs.commands['default'],$.proxy(function(idx, cmd){
					this.genShapes = this.genShapes + (this[cmd.method].apply(this, cmd.params) || '');
				}, this));
				rs.subList && this.renderList(rs.subList, rs);
				
			}, this));
			
			this.$maingroup[0].insertAdjacentHTML('beforeEnd', this.genShapes);
		},
		delegateEvents : function(){
			
			this.$vmlcontainer.mousedown(function(e){
				e = e || window.event;
				var _this = graph_renderer.drawer, $this = _this.$vmlcontainer
					,evt = $.event.fix(e)
					, mx = evt.pageX
					, my = evt.pageY
					, srcElement = e.srcElement, uuid = srcElement.className, frontRs, frontRootRs;

					
				if(uuid){
					frontRs = graph_renderer.allRenderStructure[uuid];
					frontRootRs = graph_renderer.findRootRs(frontRs);
					frontRootRs.selfShape.triggerEvent('mousedown', evt);
					if(graph_renderer.nowFocusRs !== frontRootRs){
						graph_renderer.nowFocusRs && graph_renderer.nowFocusRs.selfShape.triggerEvent('inactive', evt);
						if(frontRootRs !== frontRs){
							if(!frontRootRs.selfShape.isActive){
								frontRootRs.selfShape.triggerEvent('active', evt);
							}
						}
						if(!frontRootRs.selfShape.isActive){
							frontRootRs.selfShape.triggerEvent('active', evt);
						}
						
						graph_renderer.nowFocusRs = frontRootRs;
					}
					if(frontRootRs.selfShape.dragable){
						frontRootRs.selfShape.triggerEvent('dragstart', evt);
						graph_renderer.isDragMode = true;
						graph_renderer.dragInfo = [mx ,frontRootRs.selfShape.posX, my ,frontRootRs.selfShape.posY, _this.width - graph_renderer.nowFocusRs.selfShape.width, _this.height - graph_renderer.nowFocusRs.selfShape.height];
						if(_this.vmlcontainer.setCapture){
	                    	_this.vmlcontainer.setCapture(true);
	                    	$this.unbind('mousemove').bind('mousemove', _this.__dragHandler);
	                    	$this.bind('mouseup', _this.__dragEnd);
	                    	$this.bind('losecapture', _this.__dragEnd);
	                    }else{
	                    	window.captureEvents && window.captureEvents(Event.MOUSEMOVE | Event.MOUSEUP);
	                    	$(document).bind('mousemove', _this.__dragHandler).bind('mouseup', _this.__dragEnd);
	                    	//window.captureEvents(Event.MOUSEMOVE | Event.MOUSEUP);
	                    	window.onblur = _this.__dragEnd;
	                    }
					}else{
						$this.unbind('mouseup').bind('mouseup', _this.__normalMouseupHandler);
					}
				}else{
					graph_renderer.nowFocusRs && graph_renderer.nowFocusRs.selfShape.triggerEvent('inactive', evt, true);
					graph_renderer.nowFocusRs = null;
				}
			}).mousemove( this.__moveHandler);
		},
		_getAllParentIds : function(uuid){
			var ids = {}, tmprs;
			tmprs = graph_renderer.allRenderStructure[uuid];
			while(tmprs){
				ids[uuid] = tmprs;
				tmprs = graph_renderer.allRenderStructure[tmprs.selfShape.zcontext];
			}
			
			return ids;
		},
		_fireShapeEvents : function(etype, uuid, e){
			var thisshape = graph_renderer.allShapes[uuid];
			while(thisshape){
				thisshape.triggerEvent(etype, e);
				thisshape = graph_renderer.allShapes[thisshape.zcontext];
			}
		},
		__moveHandler : function(e){
			e = e || window.event;
			var _this = graph_renderer.drawer, evt = $.event.fix(e), srcElement = e.srcElement, uuid = srcElement.className, ses;
			if(uuid){
				ses = _this._getAllParentIds(uuid);
				$.each(ses, function(sid, s){
					if(s.selfShape.disableEvents){
						return true;
					}
					if(!checkStatus(s.status, 0)){
						s.status = setStatus(s.status, 0, 1);
						return s.selfShape.triggerEvent('mousein', evt);
					}
				});
				
				$.each(graph_renderer.allRenderStructure, function(sid, s){
					if(ses[sid]){
						return true;
					}
					if(s.selfShape.disableEvents){
						return true;
					}
					if(checkStatus(s.status, 0)){
						s.status = setStatus(s.status, 0, 0);
						return s.selfShape.triggerEvent('mouseout', evt);
					}
				});
			}
		},
		__dragHandler : function(e){
			e = e || window.event;
			var _this = graph_renderer.drawer, draggingShape = graph_renderer.nowFocusRs.selfShape
				, evt = $.event.fix(e)
				, mx = evt.pageX
				, my = evt.pageY
				;
				
			draggingShape.triggerEvent('dragging', evt, graph_renderer.dragInfo, _this.nowScale);
			draggingShape.dragged = true;
			
			e && e.preventDefault && e.preventDefault();
        	return false;
		},
		__dragEnd : function(e){
			e = e || window.event;
			var _this = graph_renderer.drawer, $this = _this.$vmlcontainer
				, draggingShape = graph_renderer.nowFocusRs.selfShape, evt = $.event.fix(e);
			if(_this.vmlcontainer.releaseCapture){
	        	_this.vmlcontainer.releaseCapture(true);
	        	$this.unbind('mousemove').bind('mousemove', _this.__moveHandler);
	        	$this.unbind('mouseup');
	        	$this.unbind('losecapture');
	        }else{
	        	$(document).unbind('mousemove').unbind('mouseup');
	        	window.releaseEvents && window.releaseEvents(Event.MOUSEMOVE | Event.MOUSEUP);
	        	window.onblur = null;
	        }
	        graph_renderer.isDragMode = false;
			graph_renderer.dragInfo = null;
			draggingShape.triggerEvent('dragend', evt);
			draggingShape.triggerEvent('mouseup', evt);
			if(!draggingShape.dragged){
				draggingShape.triggerEvent('click', evt);
			}
			
			e && e.preventDefault && e.preventDefault();
        	return false;
		},
		__normalMouseupHandler : function(e){
			e = e || window.event;
			var draggingShape = graph_renderer.nowFocusRs.selfShape, evt = $.event.fix(e);
			if(!draggingShape){
				return;	
			}
			draggingShape.triggerEvent('mouseup', evt);
			draggingShape.triggerEvent('click', evt);
		},
		clearAll : function(){
			this.$maingroup.remove();
			this.vmlcontainer.insertAdjacentHTML('beforeEnd', '<v:group coordorigin="0 0" coordsize="' + this.width + ' ' + this.height + '"' +
                    ' style="position:absolute;top:0;left:0;width:' + this.pixelWidth + 'px;height=' + this.pixelHeight + 'px;"></v:group>');
            this.$maingroup = this.$vmlcontainer.children(':last');
		}
	});
	
	ImageObj = XFrame_Graph._createClass({
		init : function(url){
			this.queue = null;
			this.ready = false;
			this.img = null;
			this.url = url;
			this.loadImg(url);
		},
		loadImg : function(url){
			this.img = this.img || (new Image());
			this.img.onload = $.proxy(this.__loadHandler, this);
			this.img.src = url;
		},
		__loadHandler : function(){
			this.img.onload = null;
			this.ready = true;
		}
	});
	
	imageCacher = {
		imgs : {},
		allReady : false,
		getImg : function(img){
			var thisImg;
			if(thisImg = this.imgs[img]){
				
				return thisImg;
			}else{
				return null;
			}
		},
		addImg : function(img){
			if(this.imgs[img]){
				return;
			}
			var newImg = new ImageObj(img);
			this.imgs[img] = newImg;
			this.allReady = false;
		},
		checkStatus : function(){
			if(this.allReady){
				return true;
			}
			var tmpres = true;
			$.each(this.imgs, function(idx, imgObj){
				return tmpres = imgObj.ready;
			});
			return this.allReady = tmpres;
		}
	};

	global && (global.XFrame_Graph = XFrame_Graph);
	
	return XFrame_Graph;
}));